using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace LS
{
    public class UnityComponentForDOTSReferences : MonoBehaviour
    {
        private static object s_Loaker = new object();

        private static UnityComponentForDOTSReferences s_Instance = null;

        public static UnityComponentForDOTSReferences Instance
        {
            get
            {
                if (s_Instance != null)
                {
                    return s_Instance;
                }
                lock (s_Loaker)
                {
                    s_Instance = UnityEngine.Object.FindObjectOfType<UnityComponentForDOTSReferences>();
                    if (s_Instance == null)
                    {
                        var go = new GameObject("Unity Object For DOTS References");
                        s_Instance = go.AddComponent<UnityComponentForDOTSReferences>();
                    }

                    return s_Instance;
                }
            }
        }

        private Dictionary<int, UnityComponentReferenceHolder> m_References = new Dictionary<int, UnityComponentReferenceHolder>();
        private Dictionary<string, UnityComponentReferenceHolder> m_ReferencesKeyMap = new Dictionary<string, UnityComponentReferenceHolder>();

        private void Awake()
        {
            if (Instance != this)
            {
                DestroyImmediate(gameObject);
                return;
            }
            DontDestroyOnLoad(this);
        }

        public bool Contains(int instanceId)
        {
            return m_References.ContainsKey(instanceId);
        }

        public bool Contains(string key)
        {
            return m_ReferencesKeyMap.ContainsKey(key);
        }

        public bool Contains(Component component)
        {
            if (component is UnityComponentReferenceHolder)
            
                return Contains((component as UnityComponentReferenceHolder).ReferenceInstanceId);
            
            else 
                return Contains(component.GetInstanceID());
        }

        public int GetReferenceId(string key)
        {
            if (m_ReferencesKeyMap.TryGetValue(key,out var value))
            {
                return value.ReferenceInstanceId;
            }
            return 0;
        }

        public Component GetReferenceById(int instanceId)
        {
            if (m_References.TryGetValue(instanceId,out var value))
            {
                return value.Reference;
            }
            return null;
        }

        public Component GetReferenceByKey(string key)
        {
            if (m_ReferencesKeyMap.TryGetValue(key, out var value))
            {
                return value.Reference;
            }
            return null;
        }

        public T GetReferenceById<T>(int instanceId)
            where T: Component
        {
            if (m_References.TryGetValue(instanceId, out var value))
            {
                return value.Reference as T;
            }
            return null;
        }

        public T GetReferenceByKey<T>(string key)
            where T : Component
        {
            if (m_ReferencesKeyMap.TryGetValue(key, out var value))
            {
                return value.Reference as T;
            }
            return null;
        }

        public int RegistReference(Component unityComponent)
        {
            if (unityComponent == null)
            {
                return 0;
            }
            int instanceId = 0;
            if (unityComponent is UnityComponentReferenceHolder)
            {
                var holder = (unityComponent as UnityComponentReferenceHolder);
                instanceId = holder.ReferenceInstanceId;
                string key = holder.ReferenceKey;
                if (!m_References.ContainsKey(instanceId))
                {
                    m_References.Add(instanceId,unityComponent as UnityComponentReferenceHolder);
                }

                if (!string.IsNullOrEmpty(key))
                {
                    m_ReferencesKeyMap[key] = unityComponent as UnityComponentReferenceHolder;
                }
                holder.OnRegist();
            }
            else
            {
                instanceId = unityComponent.GetInstanceID();
                if (!m_References.ContainsKey(instanceId))
                {
                    var holder = UnityComponentReferenceHolder.GetOrAddHolder(unityComponent);
                    m_References.Add(instanceId, holder);
                    if (!string.IsNullOrEmpty(holder.ReferenceKey))
                    {
                        m_ReferencesKeyMap[holder.ReferenceKey] = holder;
                    }
                }
            }
            return instanceId;
        }

        public void UnregistReference(string key)
        {
            if (m_ReferencesKeyMap.TryGetValue(key,out var value))
            {
                UnregistReference(value);
            }
        }

        public void UnregistReference(Component unityComponent)
        {
            int id = 0;
            if (unityComponent is UnityComponentReferenceHolder)
            {
                id = (unityComponent as UnityComponentReferenceHolder).ReferenceInstanceId;
            }
            else
            {
                id = unityComponent.GetInstanceID();
            }
            UnregistReference(id);
        }

        public void UnregistReference(int instanceId)
        {
            if (m_References.TryGetValue(instanceId,out var refrence))
            {
                m_References.Remove(instanceId);
                if (m_ReferencesKeyMap.ContainsKey(refrence.ReferenceKey))
                {
                    m_ReferencesKeyMap.Remove(refrence.ReferenceKey);
                }
                refrence.OnUnregist();
                Destroy(refrence);
            }
        }
    }
}
